home *** CD-ROM | disk | FTP | other *** search
/ TPUG - Toronto PET Users Group / TPUG Users Group CD / TPUG Users Group CD.iso / AMIGA / AMICUS / AMICUS05.ADF / IFF / iffw.c < prev    next >
C/C++ Source or Header  |  1986-04-20  |  8KB  |  222 lines

  1.  
  2. /*----------------------------------------------------------------------*
  3.  * IFFW.C  Support routines for writing IFF-85 files.          1/23/86
  4.  * (IFF is Interchange Format File.)
  5.  *
  6.  * By Jerry Morrison and Steve Shaw, Electronic Arts.
  7.  * This software is in the public domain.
  8.  *
  9.  * This version for the Commodore-Amiga computer.
  10.  *----------------------------------------------------------------------*/
  11. #include "iff/iff.h"
  12. #include "iff/gio.h"
  13.  
  14. /* ---------- IFF Writer -----------------------------------------------*/
  15.  
  16. /* A macro to test if a chunk size is definite, i.e. not szNotYetKnown.*/
  17. #define Known(size)   ( (size) != szNotYetKnown )
  18.  
  19. /* Yet another weird macro to make the source code simpler...*/
  20. #define IfIffp(expr)  {if (iffp == IFF_OKAY)  iffp = (expr);}
  21.  
  22. /* ---------- OpenWIFF -------------------------------------------------*/
  23.  
  24. IFFP OpenWIFF(file, new0, limit)  BPTR file; GroupContext *new0; LONG limit; {
  25.     register GroupContext *new = new0;
  26.     register IFFP iffp = IFF_OKAY;
  27.  
  28.     new->parent       = NULL;
  29.     new->clientFrame  = NULL;
  30.     new->file         = file;
  31.     new->position     = 0;
  32.     new->bound        = limit;
  33.     new->ckHdr.ckID   = NULL_CHUNK;  /* indicates no current chunk */
  34.     new->ckHdr.ckSize = new->bytesSoFar = 0;
  35.  
  36.     if (0 > Seek(file, 0, OFFSET_BEGINNING)) /* Go to start of the file.*/
  37.      iffp = DOS_ERROR;
  38.     else if ( Known(limit) && IS_ODD(limit) )
  39.      iffp = CLIENT_ERROR;
  40.     return(iffp);
  41.     }
  42.  
  43. /* ---------- StartWGroup ----------------------------------------------*/
  44. IFFP StartWGroup(parent, groupType, groupSize, subtype, new)
  45.       GroupContext *parent, *new; ID groupType, subtype; LONG groupSize;  {
  46.     register IFFP iffp;
  47.  
  48.     iffp = PutCkHdr(parent, groupType, groupSize);
  49.     IfIffp( IFFWriteBytes(parent, (BYTE *)&subtype, sizeof(ID)) );
  50.     IfIffp( OpenWGroup(parent, new) );
  51.     return(iffp);
  52.     }
  53.  
  54. /* ---------- OpenWGroup -----------------------------------------------*/
  55. IFFP OpenWGroup(parent0, new0)  GroupContext *parent0, *new0; {
  56.     register GroupContext *parent = parent0;
  57.     register GroupContext *new    = new0;
  58.     register LONG ckEnd;
  59.     register IFFP iffp = IFF_OKAY;
  60.  
  61.     new->parent       = parent;
  62.     new->clientFrame  = parent->clientFrame;
  63.     new->file         = parent->file;
  64.     new->position     = parent->position;
  65.     new->bound        = parent->bound;
  66.     new->ckHdr.ckID   = NULL_CHUNK;
  67.     new->ckHdr.ckSize = new->bytesSoFar = 0;
  68.  
  69.     if ( Known(parent->ckHdr.ckSize) ) {
  70.      ckEnd = new->position + ChunkMoreBytes(parent);
  71.      if ( new->bound == szNotYetKnown || new->bound > ckEnd )
  72.          new->bound = ckEnd;
  73.      };
  74.  
  75.     if ( parent->ckHdr.ckID == NULL_CHUNK || /* not currently writing a chunk*/
  76.       IS_ODD(new->position) ||
  77.          (Known(new->bound) && IS_ODD(new->bound)) )
  78.      iffp = CLIENT_ERROR;
  79.     return(iffp);
  80.     }
  81.  
  82. /* ---------- CloseWGroup ----------------------------------------------*/
  83. IFFP CloseWGroup(old0)  GroupContext *old0; {
  84.     register GroupContext *old = old0;
  85.     IFFP iffp = IFF_OKAY;
  86.  
  87.     if ( old->ckHdr.ckID != NULL_CHUNK )  /* didn't close the last chunk */
  88.      iffp = CLIENT_ERROR;
  89.     else if ( old->parent == NULL ) {     /* top level file context */
  90.      if (GWriteFlush(old->file) < 0)  iffp = DOS_ERROR;
  91.      }
  92.     else {                      /* update parent context */
  93.      old->parent->bytesSoFar += old->position - old->parent->position;
  94.      old->parent->position = old->position;
  95.      };
  96.     return(iffp);
  97.     }
  98.  
  99. /* ---------- EndWGroup ------------------------------------------------*/
  100. IFFP EndWGroup(old)  GroupContext *old;  {
  101.     register GroupContext *parent = old->parent;
  102.     register IFFP iffp;
  103.  
  104.     iffp = CloseWGroup(old);
  105.     IfIffp( PutCkEnd(parent) );
  106.     return(iffp);
  107.     }
  108.  
  109. /* ---------- PutCk ----------------------------------------------------*/
  110. IFFP PutCk(context, ckID, ckSize, data)
  111.       GroupContext *context; ID ckID; LONG ckSize; BYTE *data; {
  112.     register IFFP iffp = IFF_OKAY;
  113.  
  114.     if ( ckSize == szNotYetKnown )
  115.      iffp = CLIENT_ERROR;
  116.     IfIffp( PutCkHdr(context, ckID, ckSize) );
  117.     IfIffp( IFFWriteBytes(context, data, ckSize) );
  118.     IfIffp( PutCkEnd(context) );
  119.     return(iffp);
  120.     }
  121.  
  122. /* ---------- PutCkHdr -------------------------------------------------*/
  123. IFFP PutCkHdr(context0, ckID, ckSize)
  124.       GroupContext *context0;  ID ckID;  LONG ckSize; {
  125.     register GroupContext *context = context0;
  126.     LONG minPSize = sizeof(ChunkHeader); /* physical chunk >= minPSize bytes*/
  127.  
  128.     /* CLIENT_ERROR if we're already inside a chunk or asked to write
  129.      * other than one FORM, LIST, or CAT at the top level of a file */
  130.     /* Also, non-positive ID values are illegal and used for error codes.*/
  131.     /* (We could check for other illegal IDs...)*/
  132.     if ( context->ckHdr.ckID != NULL_CHUNK  ||  ckID <= 0 )
  133.      return(CLIENT_ERROR);
  134.     else if (context->parent == NULL)  {
  135.      switch (ckID)  {
  136.          case FORM:  case LIST:  case CAT:  break;
  137.          default: return(CLIENT_ERROR);
  138.          }
  139.      if (context->position != 0)
  140.          return(CLIENT_ERROR);
  141.      }
  142.  
  143.     if ( Known(ckSize) ) {
  144.      if ( ckSize < 0 )
  145.          return(CLIENT_ERROR);
  146.      minPSize += ckSize;
  147.      };
  148.     if ( Known(context->bound)  &&
  149.          context->position + minPSize > context->bound )
  150.      return(CLIENT_ERROR);
  151.  
  152.     context->ckHdr.ckID   = ckID;
  153.     context->ckHdr.ckSize = ckSize;
  154.     context->bytesSoFar   = 0;
  155.     if (0 >
  156.      GWrite(context->file, (BYTE *)&context->ckHdr, sizeof(ChunkHeader))
  157.      )
  158.      return(DOS_ERROR);
  159.     context->position += sizeof(ChunkHeader);
  160.     return(IFF_OKAY);
  161.     }
  162.  
  163. /* ---------- IFFWriteBytes ---------------------------------------------*/
  164. IFFP IFFWriteBytes(context0, data, nBytes)
  165.       GroupContext *context0;  BYTE *data;  LONG nBytes; {
  166.     register GroupContext *context = context0;
  167.  
  168.     if ( context->ckHdr.ckID == NULL_CHUNK  ||    /* not in a chunk */
  169.       nBytes < 0  ||                    /* negative nBytes */
  170.       (Known(context->bound)  &&        /* overflow context */
  171.          context->position + nBytes > context->bound)  ||
  172.       (Known(context->ckHdr.ckSize)  &&      /* overflow chunk */
  173.          context->bytesSoFar + nBytes > context->ckHdr.ckSize) )
  174.      return(CLIENT_ERROR);
  175.  
  176.     if (0 > GWrite(context->file, data, nBytes))
  177.      return(DOS_ERROR);
  178.  
  179.     context->bytesSoFar += nBytes;
  180.     context->position   += nBytes;
  181.     return(IFF_OKAY);
  182.     }
  183.  
  184. /* ---------- PutCkEnd -------------------------------------------------*/
  185. IFFP PutCkEnd(context0)  GroupContext *context0; {
  186.     register GroupContext *context = context0;
  187.     WORD zero = 0;  /* padding source */
  188.  
  189.     if ( context->ckHdr.ckID == NULL_CHUNK )  /* not in a chunk */
  190.      return(CLIENT_ERROR);
  191.  
  192.     if ( context->ckHdr.ckSize == szNotYetKnown ) {
  193.      /* go back and set the chunk size to bytesSoFar */
  194.      if ( 0 >
  195. GSeek(context->file, -(context->bytesSoFar + sizeof(LONG)), OFFSET_CURRENT) ||
  196.           0 >
  197. GWrite(context->file, (BYTE *)&context->bytesSoFar, sizeof(LONG))  ||
  198.           0 >
  199. GSeek(context->file, context->bytesSoFar, OFFSET_CURRENT)  )
  200.          return(DOS_ERROR);
  201.      }
  202.     else {  /* make sure the client wrote as many bytes as planned */
  203.      if ( context->ckHdr.ckSize != context->bytesSoFar )
  204.          return(CLIENT_ERROR);
  205.      };
  206.  
  207.     /* Write a pad byte if needed to bring us up to an even boundary.
  208.      * Since the context end must be even, and since we haven't
  209.      * overwritten the context, if we're on an odd position there must
  210.      * be room for a pad byte. */
  211.     if ( IS_ODD(context->bytesSoFar) ) {
  212.      if ( 0 > GWrite(context->file, (BYTE *)&zero, 1) )
  213.          return(DOS_ERROR);
  214.      context->position += 1;
  215.      };
  216.  
  217.     context->ckHdr.ckID   = NULL_CHUNK;
  218.     context->ckHdr.ckSize = context->bytesSoFar = 0;
  219.     return(IFF_OKAY);
  220.     }
  221.  
  222.